package com.zxp.server.config;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author 朱艺超
 * @version 2022.0804
 */
@Configuration
public class RedisConfig {


  @Bean
  public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

    GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
    redisCacheConfiguration = redisCacheConfiguration
        // 设置key采用String的序列化方式
        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
        //设置value序列化方式采用jackson方式序列化
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer))
        //当value为null时不进行缓存
        .disableCachingNullValues()
        // 配置缓存空间名称的前缀
        //.prefixCacheNameWith("demo:")
        //全局配置缓存过期时间【可以不配置】
        .entryTtl(Duration.ofMinutes(30L));
    //专门指定某些缓存空间的配置，如果过期时间【主要这里的key为缓存空间名称】
    Map<String, RedisCacheConfiguration> map = new HashMap<>();
    //指定特定缓存空间对应的过期时间
    map.put("user", redisCacheConfiguration.entryTtl(Duration.ofMinutes(30)));

    return RedisCacheManager
        .builder(redisConnectionFactory)
        .cacheDefaults(redisCacheConfiguration)  //默认配置
        .withInitialCacheConfigurations(map)  //某些缓存空间的特定配置
        .build();
  }


  /**
   * 自定义缓存的redis的KeyGenerator【key生成策略】
   * 注意: 该方法只是声明了key的生成策略,需在@Cacheable注解中通过keyGenerator属性指定具体的key生成策略
   * 可以根据业务情况，配置多个生成策略
   * 如: @Cacheable(value = "key", keyGenerator = "cacheKeyGenerator")
   */
  @Bean
  public KeyGenerator keyGenerator() {
    /**
     * target: 类
     * method: 方法
     * params: 方法参数
     */
    return (target, method, params) -> {
      //获取代理对象的最终目标对象
      StringBuilder sb = new StringBuilder();
      sb.append(target.getClass().getSimpleName()).append(":");
      sb.append(method.getName()).append(":");
      //调用SimpleKey的key生成器
      Object key = SimpleKeyGenerator.generateKey(params);
      return sb.append(key);
    };
  }


  /**
   * @param redisConnectionFactory：配置不同的客户端，这里注入的redis连接工厂不同： JedisConnectionFactory、LettuceConnectionFactory
   * @功能描述 ：配置Redis序列化，原因如下：
   * （1） StringRedisTemplate的序列化方式为字符串序列化，
   * RedisTemplate的序列化方式默为jdk序列化（实现Serializable接口）
   * （2） RedisTemplate的jdk序列化方式在Redis的客户端中为乱码，不方便查看，
   * 因此一般修改RedisTemplate的序列化为方式为JSON方式【建议使用GenericJackson2JsonRedisSerializer】
   */
  @Bean
  public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
    //解决value的序列化方式
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
    // 设置 key 的序列化
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    redisTemplate.setHashKeySerializer(RedisSerializer.string());
    return redisTemplate;
  }

  @Bean
  public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){
    StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
    stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
    return stringRedisTemplate;
  }



  /**
   * 此方法不能用@Ben注解，避免替换Spring容器中的同类型对象
   */
  public GenericJackson2JsonRedisSerializer serializer() {
    return new GenericJackson2JsonRedisSerializer();
  }


}
